package com.xd.common.exception;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xd.common.ascept.ResponseNotIntercept;
import com.xd.core.system.vo.Resp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.sql.SQLException;

/**
 * 全局返回
 *
 * @author Administrator
 * @date 2021/08/20
 */
@RestControllerAdvice(basePackages = {"com.xkd.core.**"}, annotations = {RestController.class})
@Slf4j
public class GlobalResponse implements ResponseBodyAdvice<Object> {

    /**
     * 支持注解@ResponseNotIntercept，使某些方法无需使用Result封装
     *
     * @param returnType    返回类型
     * @param converterType 选择的转换器类型
     * @return true 时会执行beforeBodyWrite方法，false时直接返回给前端
     */
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        if (returnType.getDeclaringClass().isAnnotationPresent(ResponseNotIntercept.class)) {
            //若在类中加了@ResponseNotIntercept 则该类中的方法不用做统一的拦截
            return false;
        }
        if (returnType.getMethod().isAnnotationPresent(ResponseNotIntercept.class)) {
            //若方法上加了@ResponseNotIntercept 则该方法不用做统一的拦截
            return false;
        }
        return true;
    }

    /**
     * 返回前
     *
     * @param o                  o
     * @param methodParameter    方法参数
     * @param mediaType          媒体类型
     * @param aClass             一个类
     * @param serverHttpRequest  服务器的http请求
     * @param serverHttpResponse 服务器的http响应
     * @return {@link Object}
     */
    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        if (methodParameter.getGenericParameterType().equals(String.class)) {
            //解决返回值为字符串时，不能正常包装
            try {
                return new ObjectMapper().writeValueAsString(Result.success(o));
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        }
        return Result.success(o);
    }

    /**
     * 异常处理程序
     *
     * @param e e
     * @return {@link Result}
     */
    @ExceptionHandler(value = XkdException.class)
    @ResponseBody
    public Resp exceptionHandler(XkdException e) {
        //打印异常
        e.printStackTrace();
        return Resp.error(e.getCode(),e.getMessage());
//        return Result.error(e.getMessage(), e.getCode());
    }

    /**
     * 异常处理程序
     *
     * @param e
     * @return {@link Result}
     */
    @ExceptionHandler(value = MaxUploadSizeExceededException.class)
    public Resp exceptionHandler(MaxUploadSizeExceededException e) {
        return Resp.error("文件过大，限制10M以内");
    }

    /**
     * 异常处理程序
     *
     * @return {@link Result}
     */
    @ExceptionHandler(value = BadSqlGrammarException.class)
    public Resp exceptionHandler(BadSqlGrammarException e) {
        e.printStackTrace();
        return Resp.error("数据库语法错误");
    }

    /**
     * 异常处理程序
     *
     * @param e e
     * @return {@link Result}
     */
    @ExceptionHandler(value = Exception.class)
    public Resp exceptionHandler(Exception e) {
        e.printStackTrace();
        String message = e.getMessage();
        if (e instanceof SQLException)
            message = "数据库错误";
        if (e instanceof DataIntegrityViolationException)
            message = "字段长度超出限制或违反唯一性约束";
        if (!(e instanceof XkdException) && !(e instanceof AuthenticationException))
            log.info("系统异常:", e);
        return Resp.error(message);
    }
}
